source('../settings/settings.R')
source('commonFunctions.R')
persons <- SELECTED_SUBJECTS
all_Drive4 <- read.csv('../data/processed/distancewise/TT1_Drive_4_30m_30m.csv')
all_Drive4$Subject <- as.factor(all_Drive4$Subject)
all_Drive4$logPerspiration <- log(all_Drive4$Perspiration)
# starting_points = c( 669 , 668 , 676 , 687 , 680 , 676 , 678 ,
# 693 , 722 , 723 , 677 , 679 , 711 , 707 ,
# 699 , 679 , 684 , 688 , 686 , 696 , 702 )
#
# ending_points = c( 741 , 786 , 749 , 782 , 736 , 756 , 768 ,
# 812 , 853 , 792 , 783 , 772 , 799 , 781 ,
# 777 , 763 , 795 , 791 , 832 , 755 , 758 )
peak_points = c( 67 , 86 , 73 , 73 , 73 , 64 , 73 ,
79 , 69 , 64 , 68 , 67 , 77 , 68 ,
82 , 67 , 72 , 72 , 71 , 68 , 64 )
# Driving time
driving_times = vector(mode="list", length = length(persons))
names(driving_times) <- persons
activity_names = vector(mode="list", length = length(persons))
names(activity_names) <- persons
acc_start_times = vector(mode="list", length = length(persons))
names(acc_start_times) <- persons
acc_end_times = vector(mode="list", length = length(persons))
names(acc_end_times) <- persons
stressor_start_times = vector(mode="list", length = length(persons))
names(stressor_start_times) <- persons
stressor_end_times = vector(mode="list", length = length(persons))
names(stressor_end_times) <- persons
complete_times = vector(mode="list", length = length(persons))
names(complete_times) <- persons
data_baseline = vector(mode="list", length=length(persons))
pp_baseline = vector(mode="list", length=length(persons))
names(data_baseline) <- persons
names(pp_baseline) <- persons
# Number of peaks
PREV_DISTANCE = 600
TRACKING_DISTANCE = 150
DRIVE_MODE = 4
getActivityName <- function(x, fullname=F) {
if(x == 1) return(ifelse(fullname, "Normal", "NO"))
if(x == 2) return(ifelse(fullname, "Cognitive", "C"))
if(x == 3) return(ifelse(fullname, "Motoric", "M"))
}
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
pAcc <- pData[pData$Failure>0.5,] # Failure = 1
acc_start_times[[p]] <- min(pAcc$Distance)
acc_end_times[[p]] <- max(pAcc$Distance)
activity_names[[p]] <- getActivityName(pData[pData$Time==60,]$Activity, fullname = T)
pStressor = pData[pData$Activity>1.5,] # Stressor = 2, 3
if (nrow(pStressor) > 0) {
stressor_start_times[[p]] <- min(pStressor$Distance)
stressor_end_times[[p]] <- max(pStressor$Distance)
} else {
stressor_start_times[[p]] <- NULL
stressor_end_times[[p]] <- NULL
}
}
idx <- 1
plt_AllAcc <- vector(mode="list", length=length(persons))
names(plt_AllAcc) <- persons
COLOR_ACC = "#02A3C8"
COLOR_PP = "#F28E8E"
COLOR_BRAKE = "#888888"
y1 <- list(
tickfont = list(color = COLOR_ACC),
title="Degree",
range=c(0, 100)
)
y2 <- list(
tickfont = list(color = COLOR_PP),
overlaying = "y",
side = "right",
title = "Log Perspiration",
showgrid = FALSE,
range=c(min(all_Drive4$ppLogNormalized), max(all_Drive4$ppLogNormalized))
)
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
# Baseline
data_baseline[[p]] <- read.csv(str_interp("../data/processed/drives/T0${person}/T0${person}_Drive_1.csv", list(person=p)))
# Compute the mean
p_pp_nr <- data_baseline[[p]]$Perspiration
p_pp_nr <- p_pp_nr[!is.na(p_pp_nr)]
pp_baseline[[p]] <- log(mean(p_pp_nr))
# Incident
driving_times[[p]] <- max(pData$Distance)
incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
incident_ending_time <- acc_end_times[[p]]
complete_times[[p]] <- ifelse(incident_starting_time + TRACKING_DISTANCE > driving_times[[p]], driving_times[[p]], incident_starting_time + TRACKING_DISTANCE)
from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
to_time <- complete_times[[p]]
# print(paste("From", from_time))
# print(paste("Incident", incident_starting_time))
# print(paste("To", to_time))
pDataBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
pDataAfter <- pData[pData$Distance >= incident_starting_time & pData$Distance <= to_time,]
# print(nrow(pDataBefore))
# print(nrow(pDataAfter))
ppMeanBefore <- mean(pDataBefore$ppLogNormalized)
ppMeanAfter <- mean(pDataAfter$ppLogNormalized)
dir.create(file.path('../figures/drive/', paste0('Drive_', DRIVE_MODE)), showWarnings = FALSE)
fname <- str_interp('../figures/drive/Drive_${drive}/P${person}.svg', list(drive=DRIVE_MODE, person=p))
pData <- pData[pData$Distance >= from_time,]
plot_Acc <- plot_ly(pData, x = ~Distance, height=400, width=900) %>%
add_trace(name="Acceleration", y = ~Acceleration, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_ACC)) %>%
add_trace(name="Brake", y = ~Braking, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_BRAKE)) %>%
add_trace(name="PP", y = ~ppLogNormalized, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_PP), yaxis = "y2") %>%
add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanBefore, yend = ppMeanBefore,
yaxis = "y2", name="Mean PP (Before Incident)",
line=list(color=COLOR_PP, dash = 'dot')) %>%
add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanAfter, yend = ppMeanAfter,
yaxis = "y2", name="Mean PP (After Incident)",
line=list(color="darkred", dash = 'dot')) %>%
# add_segments(x = min(pData$Distance) - 0.1, xend = max(pData$Distance), y = pp_baseline[[p]], yend = pp_baseline[[p]],
# yaxis = "y2", name="Baseline PP (from Drive 1)",
# line=list(color="blue", dash = 'dot')) %>%
layout(
title=paste0("Subject #", p, " (Stressor=", activity_names[[p]], ")"),
xaxis=list(title="Distance [m]", range=c(0)),
yaxis=y1,
yaxis2=y2,
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4),
shapes = list(
# Holistic period
list(type = "rect", fillcolor = "red",
line = list(color = "red"), opacity = 0.3,
x0 = incident_starting_time, x1 = incident_ending_time, xref = "x",
y0 = 0, y1 = 100, yref = "y"),
# Stressor period
list(type = "rect", fillcolor = "yellow",
line = list(color = "yellow"), opacity = 0.1,
x0 = stressor_start_times[[p]], x1 = incident_starting_time, xref = "x",
y0 = 0, y1 = 100, yref = "y"),
list(type = "rect", fillcolor = "yellow",
line = list(color = "yellow"), opacity = 0.1,
x0 = incident_ending_time, x1 = stressor_end_times[[p]], xref = "x",
y0 = 0, y1 = 100, yref = "y")
),
legend = list(x = 0.1, y = 1, bgcolor = "rgba(0,0,0,0)", title="Metric"),
autosize = F
)
# orca(plot_PP, fname)
idx <- idx + 1
plt_AllAcc[[p]] <- plot_Acc
}
htmltools::tagList(plt_AllAcc)
for (p in persons) {
# Save image
orca(plt_AllAcc[[p]], file = paste0("../plots/drive/Drive_4/T0", p, ".png"), scale = 2)
}
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
idx <- 1
behavioralColumns <- c("Subject",
"Brake_u",
"Brake_std",
"PP_before",
"PP_u",
"PP_std",
"PP_dev")
behavioralMatrix <- matrix(nrow=length(persons), ncol = length(behavioralColumns))
# Careful about Subject 09
# selected_persons <- persons[persons != "09"]
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
incident_ending_time <- acc_end_times[[p]]
from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
to_time <- complete_times[[p]]
dfBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
dfAfter <- pData[pData$Distance >= incident_starting_time & pData$Distance <= to_time,]
# diffSpeed <- mean(dfAfter$Speed) - mean(dfBefore$Speed)
brakeMean <- mean(dfAfter$Braking)
brakeStd <- mean(dfAfter$Braking)
ppMean <- mean(dfAfter$ppLogNormalized)
ppBefore <- mean(dfBefore$ppLogNormalized)
ppStd <- sd(dfAfter$ppLogNormalized)
mid_avg <- (pp_baseline[[p]] + mean(dfBefore$ppLogNormalized)) / 2
diffPP <- mean(dfAfter$ppLogNormalized) - mean(dfBefore$ppLogNormalized)
behavioralMatrix[idx, ] <- c(p,
round(brakeMean, digits=5),
round(brakeStd, digits=5),
round(ppBefore, digits=5),
round(ppMean, digits = 5),
round(ppStd, digits=5),
round(diffPP, digits=5))
idx <- idx + 1
}
# behavioralMatrix
behavioralDf <- as.data.frame(behavioralMatrix)
names(behavioralDf) <- behavioralColumns
behavioralDf
NA
clusteringDf <- behavioralDf
clusteringDf$Subject <- NULL
# clusteringDf$PP_dev_norm <- as.numeric(clusteringDf$PP_dev) / as.numeric(clusteringDf$PP_u)
# clusteringDf$PP_std_norm <- as.numeric(clusteringDf$PP_std) / as.numeric(clusteringDf$PP_u)
clusteringDf$Brake_u <- NULL
clusteringDf$Brake_std <- NULL
clusteringDf$PP_before <- NULL
clusteringDf$PP_u <- NULL
clusteringDf$PP_std <- NULL
# clusteringDf$PP_dev <- NULL
rownames(clusteringDf) <- paste0("#", persons)
for (col in names(clusteringDf)) {
clusteringDf[,col] <- as.numeric(as.character(clusteringDf[, col]))
# clusteringDf[,col] <- scale(clusteringDf[,col])
}
clusteringDf
dfActivity <- all_Drive4[all_Drive4$Time==60,] %>% select(c("Subject", "Activity"))
dfActivity$ActivityName <- sapply(dfActivity$Activity, getActivityName)
dfActivity$Subject <- as.factor(dfActivity$Subject)
rownames(dfActivity) <- NULL
dfActivity %>% select(c("Subject", "ActivityName"))
library(dendextend)
NUMBER_OF_CLUSTERS = 3
color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("blue", "darkred", color_darkpink)[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("blue", "darkred", color_darkpink)[1:NUMBER_OF_CLUSTERS]
behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0(dfActivity$ActivityName, " - #", persons)
distMatrix <- dist(behavioralMatrixClustering)
hresults <- distMatrix %>% hclust
hc <- hresults %>%
as.dendrogram %>%
set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
# set("leaves_pch", 19) %>%
# set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%
set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)
plot(hc)
legend("topright",
title="Drive=Failure \nHierachical Clustering",
legend = c("Exceptional Increase of PP" , "Slightly Increase of PP" , "No-change or Decrease of PP"),
col = c("darkred", "pink" , "blue"),
pch = c(20,20,20), bty = "n", pt.cex = 1.5, cex = 0.8 ,
text.col = "black", horiz = FALSE, inset = c(0.4, 0.1))

# Store clustering data
fPath <- str_interp("../data/processed/analysis/TT1_Drive_4_PP.csv")
dfx <- clusteringDf
dfx <- cbind(persons, dfx, dfActivity$ActivityName)
names(dfx) <- c("Subject", "PP_Dev", "Activity")
write.csv(dfx, fPath, row.names = F)
library(cluster)
fit <- kmeans(clusteringDf, 3)
clusplot(clusteringDf, fit$cluster, color=TRUE, shade=TRUE,
labels=3, lines=0)

silhouette_score <- function(k){
km <- kmeans(clusteringDf, centers = k, nstart=25)
ss <- silhouette(km$cluster, dist(clusteringDf))
mean(ss[, 3])
}
k <- 2:10
avg_sil <- sapply(k, silhouette_score)
plot(k, type='b', avg_sil, xlab='Number of clusters', ylab='Average Silhouette Scores', frame=FALSE)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNvdXJjZSgnLi4vc2V0dGluZ3Mvc2V0dGluZ3MuUicpCnNvdXJjZSgnY29tbW9uRnVuY3Rpb25zLlInKQpgYGAKCmBgYHtyfQpwZXJzb25zIDwtIFNFTEVDVEVEX1NVQkpFQ1RTCgphbGxfRHJpdmU0IDwtIHJlYWQuY3N2KCcuLi9kYXRhL3Byb2Nlc3NlZC9kaXN0YW5jZXdpc2UvVFQxX0RyaXZlXzRfMzBtXzMwbS5jc3YnKQphbGxfRHJpdmU0JFN1YmplY3QgPC0gYXMuZmFjdG9yKGFsbF9Ecml2ZTQkU3ViamVjdCkKYWxsX0RyaXZlNCRsb2dQZXJzcGlyYXRpb24gPC0gbG9nKGFsbF9Ecml2ZTQkUGVyc3BpcmF0aW9uKQoKIyBzdGFydGluZ19wb2ludHMgPSBjKCA2NjkgLCA2NjggLCA2NzYgLCA2ODcgLCA2ODAgLCAgNjc2ICwgIDY3OCAsCiMgICAgICAgICAgICAgICAgICAgICAgNjkzICwgNzIyICwgNzIzICwgNjc3ICwgNjc5ICwgIDcxMSAsICA3MDcgLCAgCiMgICAgICAgICAgICAgICAgICAgICAgNjk5ICwgNjc5ICwgNjg0ICwgNjg4ICwgNjg2ICwgIDY5NiAsICA3MDIgKQojIAojIGVuZGluZ19wb2ludHMgICA9IGMoIDc0MSAsIDc4NiAsIDc0OSAsIDc4MiAsIDczNiAsICA3NTYgLCAgNzY4ICwgIAojICAgICAgICAgICAgICAgICAgICAgIDgxMiAsIDg1MyAsIDc5MiAsIDc4MyAsIDc3MiAsICA3OTkgLCAgNzgxICwgIAojICAgICAgICAgICAgICAgICAgICAgIDc3NyAsIDc2MyAsIDc5NSAsIDc5MSAsIDgzMiAsICA3NTUgLCAgNzU4ICkKCnBlYWtfcG9pbnRzICAgICA9IGMoIDY3ICwgIDg2ICwgIDczICwgIDczICwgIDczICwgIDY0ICwgIDczICwgIAogICAgICAgICAgICAgICAgICAgICA3OSAsICA2OSAsICA2NCAsICA2OCAsICA2NyAsICA3NyAsICA2OCAsICAKICAgICAgICAgICAgICAgICAgICAgODIgLCAgNjcgLCAgNzIgLCAgNzIgLCAgNzEgLCAgNjggLCAgNjQgKQoKIyBEcml2aW5nIHRpbWUKZHJpdmluZ190aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhkcml2aW5nX3RpbWVzKSA8LSBwZXJzb25zCgphY3Rpdml0eV9uYW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhhY3Rpdml0eV9uYW1lcykgPC0gcGVyc29ucwoKYWNjX3N0YXJ0X3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGFjY19zdGFydF90aW1lcykgPC0gcGVyc29ucwphY2NfZW5kX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGFjY19lbmRfdGltZXMpIDwtIHBlcnNvbnMKCnN0cmVzc29yX3N0YXJ0X3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKHN0cmVzc29yX3N0YXJ0X3RpbWVzKSA8LSBwZXJzb25zCnN0cmVzc29yX2VuZF90aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhzdHJlc3Nvcl9lbmRfdGltZXMpIDwtIHBlcnNvbnMKCmNvbXBsZXRlX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGNvbXBsZXRlX3RpbWVzKSA8LSBwZXJzb25zCgpkYXRhX2Jhc2VsaW5lID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHBlcnNvbnMpKQpwcF9iYXNlbGluZSA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkKCm5hbWVzKGRhdGFfYmFzZWxpbmUpIDwtIHBlcnNvbnMKbmFtZXMocHBfYmFzZWxpbmUpIDwtIHBlcnNvbnMKCiMgTnVtYmVyIG9mIHBlYWtzCgpQUkVWX0RJU1RBTkNFID0gNjAwClRSQUNLSU5HX0RJU1RBTkNFID0gMTUwCkRSSVZFX01PREUgPSA0CmBgYAoKYGBge3J9CmdldEFjdGl2aXR5TmFtZSA8LSBmdW5jdGlvbih4LCBmdWxsbmFtZT1GKSB7CiAgaWYoeCA9PSAxKSByZXR1cm4oaWZlbHNlKGZ1bGxuYW1lLCAiTm9ybWFsIiwgIk5PIikpCiAgaWYoeCA9PSAyKSByZXR1cm4oaWZlbHNlKGZ1bGxuYW1lLCAiQ29nbml0aXZlIiwgIkMiKSkKICBpZih4ID09IDMpIHJldHVybihpZmVsc2UoZnVsbG5hbWUsICJNb3RvcmljIiwgIk0iKSkKfQoKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgcEFjYyA8LSBwRGF0YVtwRGF0YSRGYWlsdXJlPjAuNSxdICMgRmFpbHVyZSA9IDEKICBhY2Nfc3RhcnRfdGltZXNbW3BdXSA8LSBtaW4ocEFjYyREaXN0YW5jZSkKICBhY2NfZW5kX3RpbWVzW1twXV0gPC0gbWF4KHBBY2MkRGlzdGFuY2UpCiAgCiAgYWN0aXZpdHlfbmFtZXNbW3BdXSA8LSBnZXRBY3Rpdml0eU5hbWUocERhdGFbcERhdGEkVGltZT09NjAsXSRBY3Rpdml0eSwgZnVsbG5hbWUgPSBUKQogIAogIHBTdHJlc3NvciA9IHBEYXRhW3BEYXRhJEFjdGl2aXR5PjEuNSxdICMgU3RyZXNzb3IgPSAyLCAzCiAgaWYgKG5yb3cocFN0cmVzc29yKSA+IDApIHsKICAgIHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0gPC0gbWluKHBTdHJlc3NvciREaXN0YW5jZSkKICAgIHN0cmVzc29yX2VuZF90aW1lc1tbcF1dIDwtIG1heChwU3RyZXNzb3IkRGlzdGFuY2UpCiAgfSBlbHNlIHsKICAgIHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0gPC0gTlVMTAogICAgc3RyZXNzb3JfZW5kX3RpbWVzW1twXV0gPC0gTlVMTAogIH0KfQpgYGAKCmBgYHtyfQppZHggPC0gMQpwbHRfQWxsQWNjIDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkgCm5hbWVzKHBsdF9BbGxBY2MpIDwtIHBlcnNvbnMKCkNPTE9SX0FDQyA9ICIjMDJBM0M4IgpDT0xPUl9QUCA9ICIjRjI4RThFIgpDT0xPUl9CUkFLRSA9ICIjODg4ODg4IgoKeTEgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSBDT0xPUl9BQ0MpLAogIHRpdGxlPSJEZWdyZWUiLAogIHJhbmdlPWMoMCwgMTAwKQopCnkyIDwtIGxpc3QoCiAgdGlja2ZvbnQgPSBsaXN0KGNvbG9yID0gQ09MT1JfUFApLAogIG92ZXJsYXlpbmcgPSAieSIsCiAgc2lkZSA9ICJyaWdodCIsCiAgdGl0bGUgPSAiTG9nIFBlcnNwaXJhdGlvbiIsCiAgc2hvd2dyaWQgPSBGQUxTRSwKICByYW5nZT1jKG1pbihhbGxfRHJpdmU0JHBwTG9nTm9ybWFsaXplZCksIG1heChhbGxfRHJpdmU0JHBwTG9nTm9ybWFsaXplZCkpCikKICAKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgCiAgIyBCYXNlbGluZQogIGRhdGFfYmFzZWxpbmVbW3BdXSA8LSByZWFkLmNzdihzdHJfaW50ZXJwKCIuLi9kYXRhL3Byb2Nlc3NlZC9kcml2ZXMvVDAke3BlcnNvbn0vVDAke3BlcnNvbn1fRHJpdmVfMS5jc3YiLCBsaXN0KHBlcnNvbj1wKSkpCiAgIyBDb21wdXRlIHRoZSBtZWFuCiAgcF9wcF9uciA8LSBkYXRhX2Jhc2VsaW5lW1twXV0kUGVyc3BpcmF0aW9uCiAgcF9wcF9uciA8LSBwX3BwX25yWyFpcy5uYShwX3BwX25yKV0KICBwcF9iYXNlbGluZVtbcF1dIDwtIGxvZyhtZWFuKHBfcHBfbnIpKQogIAogICMgSW5jaWRlbnQKICBkcml2aW5nX3RpbWVzW1twXV0gPC0gbWF4KHBEYXRhJERpc3RhbmNlKQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gYWNjX3N0YXJ0X3RpbWVzW1twXV0gIyBzdGFydGluZ19wb2ludHNbaWR4XQogIGluY2lkZW50X2VuZGluZ190aW1lIDwtIGFjY19lbmRfdGltZXNbW3BdXQogIGNvbXBsZXRlX3RpbWVzW1twXV0gPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgKyBUUkFDS0lOR19ESVNUQU5DRSA+IGRyaXZpbmdfdGltZXNbW3BdXSwgZHJpdmluZ190aW1lc1tbcF1dLCBpbmNpZGVudF9zdGFydGluZ190aW1lICsgVFJBQ0tJTkdfRElTVEFOQ0UpCiAgCiAgZnJvbV90aW1lIDwtIGlmZWxzZShpbmNpZGVudF9zdGFydGluZ190aW1lIC0gUFJFVl9ESVNUQU5DRSA+PSAwLCBpbmNpZGVudF9zdGFydGluZ190aW1lIC0gUFJFVl9ESVNUQU5DRSwgMCkKICB0b190aW1lIDwtIGNvbXBsZXRlX3RpbWVzW1twXV0KICAKICAjIHByaW50KHBhc3RlKCJGcm9tIiwgZnJvbV90aW1lKSkKICAjIHByaW50KHBhc3RlKCJJbmNpZGVudCIsIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUpKQogICMgcHJpbnQocGFzdGUoIlRvIiwgdG9fdGltZSkpCiAgCiAgICAKICBwRGF0YUJlZm9yZSA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA8IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgJiBwRGF0YSREaXN0YW5jZSA+PSBmcm9tX3RpbWUsXQogIHBEYXRhQWZ0ZXIgPC0gcERhdGFbcERhdGEkRGlzdGFuY2UgPj0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJERpc3RhbmNlIDw9IHRvX3RpbWUsXQogIAogICMgcHJpbnQobnJvdyhwRGF0YUJlZm9yZSkpCiAgIyBwcmludChucm93KHBEYXRhQWZ0ZXIpKQogIAogIHBwTWVhbkJlZm9yZSA8LSBtZWFuKHBEYXRhQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkKICBwcE1lYW5BZnRlciA8LSBtZWFuKHBEYXRhQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIAogIGRpci5jcmVhdGUoZmlsZS5wYXRoKCcuLi9maWd1cmVzL2RyaXZlLycsIHBhc3RlMCgnRHJpdmVfJywgRFJJVkVfTU9ERSkpLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICBmbmFtZSA8LSBzdHJfaW50ZXJwKCcuLi9maWd1cmVzL2RyaXZlL0RyaXZlXyR7ZHJpdmV9L1Ake3BlcnNvbn0uc3ZnJywgbGlzdChkcml2ZT1EUklWRV9NT0RFLCBwZXJzb249cCkpIAogIAogIHBEYXRhIDwtIHBEYXRhW3BEYXRhJERpc3RhbmNlID49IGZyb21fdGltZSxdCiAgcGxvdF9BY2MgPC0gcGxvdF9seShwRGF0YSwgeCA9IH5EaXN0YW5jZSwgaGVpZ2h0PTQwMCwgd2lkdGg9OTAwKSAlPiUKICAgICAgICAgICAgICBhZGRfdHJhY2UobmFtZT0iQWNjZWxlcmF0aW9uIiwgeSA9IH5BY2NlbGVyYXRpb24sIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBsaW5lPWxpc3Qod2lkdGg9MS41LCBjb2xvcj1DT0xPUl9BQ0MpKSAlPiUgCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IkJyYWtlIiwgeSA9IH5CcmFraW5nLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfQlJBS0UpKSAlPiUKICAgICAgICAgICAgICBhZGRfdHJhY2UobmFtZT0iUFAiLCB5ID0gfnBwTG9nTm9ybWFsaXplZCwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIGxpbmU9bGlzdCh3aWR0aD0xLjUsIGNvbG9yPUNPTE9SX1BQKSwgeWF4aXMgPSAieTIiKSAlPiUgCiAgICAgICAgICAgICAgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwTWVhbkJlZm9yZSwgeWVuZCA9IHBwTWVhbkJlZm9yZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gInkyIiwgbmFtZT0iTWVhbiBQUCAoQmVmb3JlIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj1DT0xPUl9QUCwgZGFzaCA9ICdkb3QnKSkgJT4lCiAgICAgICAgICAgICAgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwTWVhbkFmdGVyLCB5ZW5kID0gcHBNZWFuQWZ0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9Ik1lYW4gUFAgKEFmdGVyIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj0iZGFya3JlZCIsIGRhc2ggPSAnZG90JykpICU+JQogICAgICAgICAgICAgICMgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpIC0gMC4xLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwX2Jhc2VsaW5lW1twXV0sIHllbmQgPSBwcF9iYXNlbGluZVtbcF1dLCAKICAgICAgICAgICAgICAjICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9IkJhc2VsaW5lIFBQIChmcm9tIERyaXZlIDEpIiwKICAgICAgICAgICAgICAjICAgICAgICAgICAgICBsaW5lPWxpc3QoY29sb3I9ImJsdWUiLCBkYXNoID0gJ2RvdCcpKSAlPiUKICAgIAogICAgICAgICAgICAgIGxheW91dCgKICAgICAgICAgICAgICAgIHRpdGxlPXBhc3RlMCgiU3ViamVjdCAjIiwgcCwgIiAoU3RyZXNzb3I9IiwgYWN0aXZpdHlfbmFtZXNbW3BdXSwgIikiKSwgCiAgICAgICAgICAgICAgICB4YXhpcz1saXN0KHRpdGxlPSJEaXN0YW5jZSBbbV0iLCByYW5nZT1jKDApKSwgCiAgICAgICAgICAgICAgICB5YXhpcz15MSwgCiAgICAgICAgICAgICAgICB5YXhpczI9eTIsIAogICAgICAgICAgICAgICAgbWFyZ2luID0gbGlzdChsID0gNTAsIHIgPSA1MCwgYiA9IDUwLCB0ID0gNTAsIHBhZCA9IDQpLAogICAgICAgICAgICAgICAgc2hhcGVzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgIyBIb2xpc3RpYyBwZXJpb2QKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAicmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiksIG9wYWNpdHkgPSAwLjMsCiAgICAgICAgICAgICAgICAgICAgICB4MCA9IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUsIHgxID0gaW5jaWRlbnRfZW5kaW5nX3RpbWUsIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKSwKICAgICAgICAgICAgICAgICAgIyBTdHJlc3NvciBwZXJpb2QKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAieWVsbG93IiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAieWVsbG93IiksIG9wYWNpdHkgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICB4MCA9IHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0sIHgxID0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSwgeHJlZiA9ICJ4IiwKICAgICAgICAgICAgICAgICAgICAgIHkwID0gMCwgeTEgPSAxMDAsIHlyZWYgPSAieSIpLAogICAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSAicmVjdCIsIGZpbGxjb2xvciA9ICJ5ZWxsb3ciLCAKICAgICAgICAgICAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJ5ZWxsb3ciKSwgb3BhY2l0eSA9IDAuMSwKICAgICAgICAgICAgICAgICAgICAgIHgwID0gaW5jaWRlbnRfZW5kaW5nX3RpbWUsIHgxID0gc3RyZXNzb3JfZW5kX3RpbWVzW1twXV0sIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDAuMSwgeSA9IDEsIGJnY29sb3IgPSAicmdiYSgwLDAsMCwwKSIsIHRpdGxlPSJNZXRyaWMiKSwKICAgICAgICAgICAgICAgIGF1dG9zaXplID0gRgogICAgICAgICAgICAgICkKICAKICAjIG9yY2EocGxvdF9QUCwgZm5hbWUpCiAgaWR4IDwtIGlkeCArIDEKICBwbHRfQWxsQWNjW1twXV0gPC0gcGxvdF9BY2MKfQoKaHRtbHRvb2xzOjp0YWdMaXN0KHBsdF9BbGxBY2MpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmZvciAocCBpbiBwZXJzb25zKSB7CiAgIyBTYXZlIGltYWdlCiAgb3JjYShwbHRfQWxsQWNjW1twXV0sIGZpbGUgPSBwYXN0ZTAoIi4uL3Bsb3RzL2RyaXZlL0RyaXZlXzQvVDAiLCBwLCAiLnBuZyIpLCBzY2FsZSA9IDIpCn0KYGBgCgpgYGB7cn0KaWR4IDwtIDEKYmVoYXZpb3JhbENvbHVtbnMgPC0gYygiU3ViamVjdCIsIAogICAgICAgICAgICAgICAgICAgICAgICJCcmFrZV91IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIkJyYWtlX3N0ZCIsIAogICAgICAgICAgICAgICAgICAgICAgICJQUF9iZWZvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICJQUF91IiwgIAogICAgICAgICAgICAgICAgICAgICAgICJQUF9zdGQiLAogICAgICAgICAgICAgICAgICAgICAgICJQUF9kZXYiKQpiZWhhdmlvcmFsTWF0cml4IDwtIG1hdHJpeChucm93PWxlbmd0aChwZXJzb25zKSwgbmNvbCA9IGxlbmd0aChiZWhhdmlvcmFsQ29sdW1ucykpCgojIENhcmVmdWwgYWJvdXQgU3ViamVjdCAwOQojIHNlbGVjdGVkX3BlcnNvbnMgPC0gcGVyc29uc1twZXJzb25zICE9ICIwOSJdCgpmb3IgKHAgaW4gcGVyc29ucykgewogIHBEYXRhIDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRTdWJqZWN0PT1hcy5pbnRlZ2VyKHApIHwgYWxsX0RyaXZlNCRTdWJqZWN0PT1wLF0KICAKICBpbmNpZGVudF9zdGFydGluZ190aW1lIDwtIGFjY19zdGFydF90aW1lc1tbcF1dICMgc3RhcnRpbmdfcG9pbnRzW2lkeF0KICBpbmNpZGVudF9lbmRpbmdfdGltZSA8LSBhY2NfZW5kX3RpbWVzW1twXV0KICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSBQUkVWX0RJU1RBTkNFID49IDAsIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSBQUkVWX0RJU1RBTkNFLCAwKQogIHRvX3RpbWUgPC0gY29tcGxldGVfdGltZXNbW3BdXQogICAgCiAgZGZCZWZvcmUgPC0gcERhdGFbcERhdGEkRGlzdGFuY2UgPCBpbmNpZGVudF9zdGFydGluZ190aW1lICYgcERhdGEkRGlzdGFuY2UgPj0gZnJvbV90aW1lLF0KICBkZkFmdGVyIDwtIHBEYXRhW3BEYXRhJERpc3RhbmNlID49IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgJiBwRGF0YSREaXN0YW5jZSA8PSB0b190aW1lLF0KICAKICAjIGRpZmZTcGVlZCA8LSBtZWFuKGRmQWZ0ZXIkU3BlZWQpIC0gbWVhbihkZkJlZm9yZSRTcGVlZCkKICBicmFrZU1lYW4gPC0gbWVhbihkZkFmdGVyJEJyYWtpbmcpCiAgYnJha2VTdGQgPC0gbWVhbihkZkFmdGVyJEJyYWtpbmcpCiAgCiAgcHBNZWFuIDwtIG1lYW4oZGZBZnRlciRwcExvZ05vcm1hbGl6ZWQpCiAgcHBCZWZvcmUgPC0gbWVhbihkZkJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpCiAgcHBTdGQgPC0gc2QoZGZBZnRlciRwcExvZ05vcm1hbGl6ZWQpCiAgCiAgbWlkX2F2ZyA8LSAocHBfYmFzZWxpbmVbW3BdXSArIG1lYW4oZGZCZWZvcmUkcHBMb2dOb3JtYWxpemVkKSkgLyAyCiAgICAKICBkaWZmUFAgPC0gbWVhbihkZkFmdGVyJHBwTG9nTm9ybWFsaXplZCkgLSBtZWFuKGRmQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkKICAKICBiZWhhdmlvcmFsTWF0cml4W2lkeCwgXSA8LSBjKHAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYnJha2VNZWFuLCBkaWdpdHM9NSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYnJha2VTdGQsIGRpZ2l0cz01KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBwQmVmb3JlLCBkaWdpdHM9NSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwcE1lYW4sIGRpZ2l0cyA9IDUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocHBTdGQsIGRpZ2l0cz01KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGRpZmZQUCwgZGlnaXRzPTUpKQogIGlkeCA8LSBpZHggKyAxCn0KCiMgYmVoYXZpb3JhbE1hdHJpeAoKYmVoYXZpb3JhbERmIDwtIGFzLmRhdGEuZnJhbWUoYmVoYXZpb3JhbE1hdHJpeCkKbmFtZXMoYmVoYXZpb3JhbERmKSA8LSBiZWhhdmlvcmFsQ29sdW1ucwoKYmVoYXZpb3JhbERmCgpgYGAKCgpgYGB7cn0KY2x1c3RlcmluZ0RmIDwtIGJlaGF2aW9yYWxEZgpjbHVzdGVyaW5nRGYkU3ViamVjdCA8LSBOVUxMCiMgY2x1c3RlcmluZ0RmJFBQX2Rldl9ub3JtIDwtIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX2RldikgLyBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF91KQojIGNsdXN0ZXJpbmdEZiRQUF9zdGRfbm9ybSA8LSBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF9zdGQpIC8gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfdSkKY2x1c3RlcmluZ0RmJEJyYWtlX3UgPC0gTlVMTApjbHVzdGVyaW5nRGYkQnJha2Vfc3RkIDwtIE5VTEwKY2x1c3RlcmluZ0RmJFBQX2JlZm9yZSA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRQUF91IDwtIE5VTEwKY2x1c3RlcmluZ0RmJFBQX3N0ZCA8LSBOVUxMCiMgY2x1c3RlcmluZ0RmJFBQX2RldiA8LSBOVUxMCgpyb3duYW1lcyhjbHVzdGVyaW5nRGYpIDwtIHBhc3RlMCgiIyIsIHBlcnNvbnMpCgpmb3IgKGNvbCBpbiBuYW1lcyhjbHVzdGVyaW5nRGYpKSB7CiAgY2x1c3RlcmluZ0RmWyxjb2xdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGNsdXN0ZXJpbmdEZlssIGNvbF0pKQogICMgY2x1c3RlcmluZ0RmWyxjb2xdIDwtIHNjYWxlKGNsdXN0ZXJpbmdEZlssY29sXSkKfQpjbHVzdGVyaW5nRGYKYGBgCgpgYGB7cn0KZGZBY3Rpdml0eSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkVGltZT09NjAsXSAlPiUgc2VsZWN0KGMoIlN1YmplY3QiLCAiQWN0aXZpdHkiKSkKZGZBY3Rpdml0eSRBY3Rpdml0eU5hbWUgPC0gc2FwcGx5KGRmQWN0aXZpdHkkQWN0aXZpdHksIGdldEFjdGl2aXR5TmFtZSkKZGZBY3Rpdml0eSRTdWJqZWN0IDwtIGFzLmZhY3RvcihkZkFjdGl2aXR5JFN1YmplY3QpCnJvd25hbWVzKGRmQWN0aXZpdHkpIDwtIE5VTEwKZGZBY3Rpdml0eSAlPiUgc2VsZWN0KGMoIlN1YmplY3QiLCAiQWN0aXZpdHlOYW1lIikpCmBgYAoKYGBge3J9CmxpYnJhcnkoZGVuZGV4dGVuZCkKCk5VTUJFUl9PRl9DTFVTVEVSUyA9IDMKCmNvbG9yX2RhcmtwaW5rID0gIiNlNzU0ODAiCkNMVVNURVJfQlJBTkNIX0NPTE9SUyA8LSBjKCJibHVlIiwgImRhcmtyZWQiLCBjb2xvcl9kYXJrcGluaylbMTpOVU1CRVJfT0ZfQ0xVU1RFUlNdCkNMVVNURVJfTEFCRUxfQ09MT1JTIDwtIGMoImJsdWUiLCAiZGFya3JlZCIsIGNvbG9yX2RhcmtwaW5rKVsxOk5VTUJFUl9PRl9DTFVTVEVSU10KCmJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nIDwtIGFzLm1hdHJpeChjbHVzdGVyaW5nRGYpCnJvd25hbWVzKGJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nKSA8LSBwYXN0ZTAoZGZBY3Rpdml0eSRBY3Rpdml0eU5hbWUsICIgLSAjIiwgcGVyc29ucykKZGlzdE1hdHJpeCA8LSBkaXN0KGJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nKQpocmVzdWx0cyA8LSBkaXN0TWF0cml4ICU+JSBoY2x1c3QKCmhjIDwtIGhyZXN1bHRzICU+JSAKICAgICAgYXMuZGVuZHJvZ3JhbSAlPiUKICAgICAgc2V0KCJub2Rlc19jZXgiLCBOVU1CRVJfT0ZfQ0xVU1RFUlMpICU+JQogICAgICBzZXQoImxhYmVsc19jb2wiLCB2YWx1ZSA9IENMVVNURVJfTEFCRUxfQ09MT1JTLCBrPU5VTUJFUl9PRl9DTFVTVEVSUykgJT4lCiAgICAgICMgc2V0KCJsZWF2ZXNfcGNoIiwgMTkpICU+JQogICAgICAjIHNldCgibGVhdmVzX2NvbCIsIHZhbHVlID0gYygiZ3JheSIpLCBrPU5VTUJFUl9PRl9DTFVTVEVSUykgJT4lICAgIAogICAgICBzZXQoImJyYW5jaGVzX2tfY29sb3IiLCB2YWx1ZT1DTFVTVEVSX0JSQU5DSF9DT0xPUlMsIGs9TlVNQkVSX09GX0NMVVNURVJTKQoKcGxvdChoYykKbGVnZW5kKCJ0b3ByaWdodCIsIAogICAgIHRpdGxlPSJEcml2ZT1GYWlsdXJlIFxuSGllcmFjaGljYWwgQ2x1c3RlcmluZyIsCiAgICAgbGVnZW5kID0gYygiRXhjZXB0aW9uYWwgSW5jcmVhc2Ugb2YgUFAiICwgIlNsaWdodGx5IEluY3JlYXNlIG9mIFBQIiAsICJOby1jaGFuZ2Ugb3IgRGVjcmVhc2Ugb2YgUFAiKSwgCiAgICAgY29sID0gYygiZGFya3JlZCIsICJwaW5rIiAsICJibHVlIiksCiAgICAgcGNoID0gYygyMCwyMCwyMCksIGJ0eSA9ICJuIiwgIHB0LmNleCA9IDEuNSwgY2V4ID0gMC44ICwgCiAgICAgdGV4dC5jb2wgPSAiYmxhY2siLCBob3JpeiA9IEZBTFNFLCBpbnNldCA9IGMoMC40LCAwLjEpKQpgYGAKCmBgYHtyfQojIFN0b3JlIGNsdXN0ZXJpbmcgZGF0YQpmUGF0aCA8LSBzdHJfaW50ZXJwKCIuLi9kYXRhL3Byb2Nlc3NlZC9hbmFseXNpcy9UVDFfRHJpdmVfNF9QUC5jc3YiKQpkZnggPC0gY2x1c3RlcmluZ0RmCmRmeCA8LSBjYmluZChwZXJzb25zLCBkZngsIGRmQWN0aXZpdHkkQWN0aXZpdHlOYW1lKQpuYW1lcyhkZngpIDwtIGMoIlN1YmplY3QiLCAiUFBfRGV2IiwgIkFjdGl2aXR5IikKd3JpdGUuY3N2KGRmeCwgZlBhdGgsIHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKZml0IDwtIGttZWFucyhjbHVzdGVyaW5nRGYsIDMpCmNsdXNwbG90KGNsdXN0ZXJpbmdEZiwgZml0JGNsdXN0ZXIsIGNvbG9yPVRSVUUsIHNoYWRlPVRSVUUsCiAgIGxhYmVscz0zLCBsaW5lcz0wKQpgYGAKCmBgYHtyfQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspewogIGttIDwtIGttZWFucyhjbHVzdGVyaW5nRGYsIGNlbnRlcnMgPSBrLCBuc3RhcnQ9MjUpCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyLCBkaXN0KGNsdXN0ZXJpbmdEZikpCiAgbWVhbihzc1ssIDNdKQp9CmsgPC0gMjoxMAphdmdfc2lsIDwtIHNhcHBseShrLCBzaWxob3VldHRlX3Njb3JlKQpwbG90KGssIHR5cGU9J2InLCBhdmdfc2lsLCB4bGFiPSdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiPSdBdmVyYWdlIFNpbGhvdWV0dGUgU2NvcmVzJywgZnJhbWU9RkFMU0UpCmBgYAoKCg==